Kata
Create a function method that allow you to wrap an existing function. The method signature would look something like this:
Usage Example:
function speak(name){
return "Hello " + name;
}
speak = speak.wrap(function(original, yourName, myName){
greeting = original(yourName);
return greeting + ", my name is " + myName;
})
var greeting = speak("Mary", "Kate");
这个题目就是给函数扩展一个 wrap
方法,wrap 本意是「包裹」,这里可以理解为,在原由函数功能的基础上,再次添加新的功能,可以视为对原有函数的一种增强。在本例中,speak
是一个简单函数,通过 wrap
方法,使之在 speak
基础之上,增加了 greeting 的功能。
Solutions
1. that = this
Function.prototype.wrap = function(fn) {
var that = this;
return function(arg2,arg3) {
return fn(that,arg2,arg3);
};
}
因为 wrap
方法的参数是一个 callback 函数,其中 original
参数是要能够映射到原函数中才行。这里涉及到 this
keyword, scope 以及函数调用的问题。
在 JavaScript 中,函数调用有三种情况:
-
fn(arg)
-
obj.child.method(arg)
- ``fn.call(context,arg)`
但其实第三种调用方法才是最通用的书写形式,前两种都可以用第三种书写形式写出来,比如第一种,可以写成 fn.call(undefined,arg)
, 第二种可以写成 obj.child.method.call(obj.child,arg)
。因此当 wrap
作为函数的方法被函数 speak
调用时,this
指代的为函数 speak
。通过创建变量并赋值,将这种映射关系存储下来。
2. bind()
Function.prototype.wrap = function(callback) {
return callback.bind(this, this);
}
bind()
方法首先会创建一个新函数,这个新函数的函数体和 callback
保持一致。 bind()
方法本身可能带多个参数,第一个参数用于指定 this
的值,之后的参数作为「预制参数」传入到新函数中。预制参数的含义可以查阅文末的参考链接。
而在这个例子里,bind()
方法的参数是两个 this
,第一个 this
仅仅指定 context,因为是 speak()
调用的 wrap()
,所以 this
就指向 speak()
。 第二个 this
则作为 callback
函数第一个参数的预制参数,即参数 original
。所以 original
表示函数 speak()
。剩余参数则通过实参(实际调用时使用的参数)进行传入。为了便于理解其中的参数传递,可以试着做以下实验:
function speak(name){
return "Hello " + name;
}
Function.prototype.wrap = function(callback) {
return callback.bind('context',this, 's', 'this');
}
speak = speak.wrap(function(original, yourName, myName){
console.log(original,yourName,myName)
return ", my name is " + myName;
})
console.log(speak("Mary", "Kate")) //
随意改变 callback.bind()
的参数值,查看对应的 console.log(original,yourName,myName)
的输出。可以找到规律,bind()
方法的第一个参数仅仅指定 context,对参数传递无影响。除了第一个之外的其他参数作为预制参数,影响参数传递。
tl;dr:
bind()
方法第一个参数对应原函数( bound function )的 this
,第二个参数对应原函数( bound function )的第一个参数,但三个参数对应原函数的第二个参数,以此类推。
3. arrow function
Function.prototype.wrap = function (f) {
return (...args) => f(this, ...args)
};
…args
表明 rest paramaters,其余参数,字面意思很好理解。=>
为 arrow function,类似 lambda 函数,这里 return 一个新函数,新函数的第一个参数值改为 this
,其余参数作为实参传入。原理上这种方法也是类似于预制参数。
参考: